`timescale 1ns/1ps

module adder_tree_1 #(
    parameter NUM_INPUTS = 128,
    parameter DATA_WIDTH = 16,
    parameter OUT_WIDTH  = 32          // 输出累加宽度
)(
    input  wire                         clk,
    input  wire                         rst_n,
    input  wire [NUM_INPUTS*DATA_WIDTH-1:0] data,
    output reg  [OUT_WIDTH-1:0]         sum_out
);
    // Stage1: 128 -> 64
    wire signed [OUT_WIDTH-1:0] s1 [0:63];
    genvar i;
    generate
        for(i=0;i<64;i=i+1) begin: G_L1
            wire signed [DATA_WIDTH-1:0] a = data[(2*i)*DATA_WIDTH +: DATA_WIDTH];
            wire signed [DATA_WIDTH-1:0] b = data[(2*i+1)*DATA_WIDTH +: DATA_WIDTH];
            assign s1[i] = {{(OUT_WIDTH-DATA_WIDTH){a[DATA_WIDTH-1]}},a} +
                           {{(OUT_WIDTH-DATA_WIDTH){b[DATA_WIDTH-1]}},b};
        end
    endgenerate
    reg signed [OUT_WIDTH-1:0] r1 [0:63];
    integer k1;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            for(k1=0;k1<64;k1=k1+1) r1[k1] <= 0;
        else
            for(k1=0;k1<64;k1=k1+1) r1[k1] <= s1[k1];
    end

    // Stage2: (64 -> 32 ->16 ->8)  3级组合压缩，再打一拍
    wire signed [OUT_WIDTH-1:0] s2_32 [0:31];
    generate
        for(i=0;i<32;i=i+1) begin: G_L2
            assign s2_32[i] = r1[2*i] + r1[2*i+1];
        end
    endgenerate
    wire signed [OUT_WIDTH-1:0] s3_16 [0:15];
    generate
        for(i=0;i<16;i=i+1) begin: G_L3
            assign s3_16[i] = s2_32[2*i] + s2_32[2*i+1];
        end
    endgenerate
    wire signed [OUT_WIDTH-1:0] s4_8 [0:7];
    generate
        for(i=0;i<8;i=i+1) begin: G_L4
            assign s4_8[i] = s3_16[2*i] + s3_16[2*i+1];
        end
    endgenerate
    reg signed [OUT_WIDTH-1:0] r2 [0:7];
    integer k2;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            for(k2=0;k2<8;k2=k2+1) r2[k2] <= 0;
        else
            for(k2=0;k2<8;k2=k2+1) r2[k2] <= s4_8[k2];
    end

    // Stage3: (8 ->4 ->2 ->1) 再打一拍输出
    wire signed [OUT_WIDTH-1:0] s5_4 [0:3];
    generate
        for(i=0;i<4;i=i+1) begin: G_L5
            assign s5_4[i] = r2[2*i] + r2[2*i+1];
        end
    endgenerate
    wire signed [OUT_WIDTH-1:0] s6_2 [0:1];
    assign s6_2[0] = s5_4[0] + s5_4[1];
    assign s6_2[1] = s5_4[2] + s5_4[3];
    wire signed [OUT_WIDTH-1:0] s7_1 = s6_2[0] + s6_2[1];

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            sum_out <= 0;
        else
            sum_out <= s7_1;
    end
endmodule